package principal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import util.Aleatorio;

import elementosEvolutivos.Cromosoma;
import elementosEvolutivos.Evaluador;
import elementosEvolutivos.FuncAdaptacion;
import elementosEvolutivos.FuncCruce;
import elementosEvolutivos.FuncMutacion;
import elementosEvolutivos.FuncSeleccion;

public class AlgoritmoEvolutivo {
	
	private boolean tipo_max;
	private Cromosoma crom;
	private Evaluador fun_eval;
	private FuncAdaptacion fun_adapt;
	private FuncSeleccion fun_selec;
	private FuncCruce fun_cruce;
	private FuncMutacion fun_mutacion;
	private Collection<Cromosoma> poblacion;
	private int num_individuos;
	private int num_generaciones;
	private double porc_cruce;
	private double porc_mutacion;
	private Cromosoma mejor_individuo;
	private double[] mejor_absoluto;
	private double[] mejor_generacion;
	private double[] media_generacion;
	private int tam_elite;
	private Collection<Cromosoma> elite;
	private boolean pobIniFija;
	
	public Cromosoma getMejorIndividuo(){
		return mejor_individuo;
	}
	
	public double[] getMejor_absoluto() {
		return mejor_absoluto;
	}



	public double[] getMejor_generacion() {
		return mejor_generacion;
	}



	public double[] getMedia_generacion() {
		return media_generacion;
	}



	public void configurar(
			Cromosoma cr, boolean es_max, FuncAdaptacion fa, Evaluador ev, FuncSeleccion sel,
			FuncCruce fc, FuncMutacion fm, int ni, int ng, double pc, double pm, int elite, boolean pobIniFija){
		crom = cr;
		tipo_max = es_max;
		fun_adapt = fa;
		fun_eval = ev;
		fun_selec = sel;
		fun_cruce = fc;
		fun_mutacion = fm;
		poblacion = new ArrayList<Cromosoma>();
		num_individuos = ni;
		num_generaciones = ng;
		porc_cruce = pc;
		porc_mutacion = pm;
		mejor_absoluto = new double[ng];
		mejor_generacion = new double[ng];
		media_generacion = new double[ng];
		tam_elite = (int)(((double)elite/100.0)*num_individuos);
		this.elite = new ArrayList<Cromosoma>();
		this.pobIniFija = pobIniFija;
	}
	
	public void ejecutar(){
		inicializar();
		for(int i=1; i<num_generaciones; i++){
			seleccion();
			reproduccion();
			mutacion();
			reemplazamiento();
			evaluacion(i);
			Controlador.getInstance().setProgreso((i+1)*100/num_generaciones);
		}
		
		Controlador.getInstance().mostrarResultado(mejor_individuo);
	}
	
	private void inicializar(){
		poblacion.clear();
		if(pobIniFija){
			Aleatorio.fijarSemilla(837839);
		}
		for(int i=0; i<num_individuos; i++){
			Cromosoma cr = crom.clone();
			cr.inicializar();
			cr.setEvaluacion(fun_eval.evaluar(cr));
			poblacion.add(cr);
		}
		mejor_individuo = ((ArrayList<Cromosoma>)poblacion).get(0);
		evaluacion(0);
		Aleatorio.fijarSemilla(System.currentTimeMillis());
	}
	
	private void adaptacion(){
		fun_adapt.adaptar(poblacion);
	}
	
	private void evaluacion(int n){
		double punt_acum=0;
		double suma_apt=0;
		double media=0;
		Cromosoma mejor_poblacion;
		adaptacion();
		Collections.sort((ArrayList<Cromosoma>) poblacion);
		for(Cromosoma cr:poblacion){
			suma_apt += cr.getAptitud();
			media += cr.getEvaluacion();
		}
		media = media/num_individuos;
		mejor_poblacion = ((ArrayList<Cromosoma>)poblacion).get(0);
		for(Cromosoma cr:poblacion){
			cr.setPuntuacion(cr.getAptitud()/suma_apt);
			cr.setPunt_acum(cr.getPuntuacion() + punt_acum);
			punt_acum += cr.getPuntuacion();
			if(tipo_max){
				if(mejor_individuo.getEvaluacion()<cr.getEvaluacion())
					mejor_individuo=cr.clone();
			}
			else{
				if(mejor_individuo.getEvaluacion()>cr.getEvaluacion())
					mejor_individuo=cr.clone();
			}
			if(mejor_poblacion.getAptitud()<cr.getAptitud())
				mejor_poblacion=cr.clone();
		}
		if(tam_elite>0)
			seleccionarElite();
		media_generacion[n] = media;
		mejor_generacion[n] = mejor_poblacion.getEvaluacion();
		mejor_absoluto[n] = mejor_individuo.getEvaluacion();
	}
	
	private void seleccionarElite() {
		Collections.sort((List<Cromosoma>) poblacion);
		((ArrayList<Cromosoma>)elite).clear();
		for(int i=0; i<tam_elite; i++ ){
			Cromosoma cr = ((ArrayList<Cromosoma>)poblacion).get(i).clone();
			elite.add(cr);
		}
	}

	
	private void seleccion(){
		poblacion = fun_selec.seleccionar(poblacion, num_individuos - tam_elite);
	}
	
	private void reproduccion(){
		ArrayList<Cromosoma> progenitores = new ArrayList<Cromosoma>();
		double prob_repr;
		for(Cromosoma c: poblacion){
			prob_repr =  Aleatorio.getDoubleAleatorio();
			if(prob_repr<porc_cruce){
				progenitores.add(c);
			}
		}
		if(progenitores.size()%2==1){
			progenitores.remove(progenitores.size()-1);
		}
		
		for(Cromosoma c: progenitores){
			poblacion.remove(c);
		}
		
		for(int i=0;i<progenitores.size()-1;i+=2){
			Cromosoma[] hijos;
			hijos = fun_cruce.cruce(progenitores.get(i),progenitores.get(i+1));
			hijos[0].setEvaluacion(fun_eval.evaluar(hijos[0]));
			hijos[1].setEvaluacion(fun_eval.evaluar(hijos[1]));
			poblacion.add(hijos[0]);
			poblacion.add(hijos[1]);
		}
	}
	
	private void mutacion(){
		for(Cromosoma c: poblacion){
			if(Aleatorio.getDoubleAleatorio()<porc_mutacion){
				fun_mutacion.mutar(c);
				c.setEvaluacion(fun_eval.evaluar(c));	
			}
		}
	}
	
	private void reemplazamiento(){
		((ArrayList<Cromosoma>)poblacion).addAll(elite);
	}
}
